// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... namespace LargoCommon.Music { using LargoCommon.Interfaces; using System; using System.Collections; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Serialization; /// /// Figural Structure. /// public class FiguralStructure : GeneralOwner, IComparable, IGeneralStruct { #region Fields /// /// Structural Code. /// private string structuralCode; /// /// General system. /// private GeneralSystem gsystem; //// readonly /// Number of nonzero bits, number of rotations. private byte level; /// List of elements. private Collection elemList; /// List of differences. private Collection diffList; #endregion #region Constructors /// Initializes a new instance of the FiguralStructure class. Serializable. public FiguralStructure() { this.elemList = new Collection(); } /// /// Initializes a new instance of the FiguralStructure class. /// /// The given system. /// Number of instance. public FiguralStructure(GeneralSystem givenSystem, decimal number) : this() { Contract.Requires(givenSystem != null); this.gsystem = givenSystem; this.DecimalNumber = number; this.SetElements(); // 20081224 //// this.CheckInstance(); } /// /// Initializes a new instance of the FiguralStructure class. /// /// The given system. /// Structural code. public FiguralStructure(GeneralSystem givenSystem, string givenStructuralCode) : this() { Contract.Requires(givenSystem != null); this.gsystem = givenSystem; for (byte e = 0; e < this.gsystem.Order; e++) { this.ElementList.Add(0); } this.SetStructuralCode(givenStructuralCode); //// this.SetElements(); // 20081224 //// 2014/12 Time optimization //// this.CheckInstance(); } /// Initializes a new instance of the FiguralStructure class. /// Figural structure. public FiguralStructure(FiguralStructure structure) : this() { Contract.Requires(structure != null); this.gsystem = structure.GSystem; this.level = structure.Level; this.GLevel = structure.GLevel; this.SetElements(); //// 2014/12 Time optimization //// this.CheckInstance(); } #endregion #region Properties /// Gets or sets DecimalNumber. /// Property description. public decimal DecimalNumber { get; set; } /// Gets or sets abstract G-System. /// Property description. [XmlIgnore] public GeneralSystem GSystem { get { Contract.Ensures(Contract.Result() != null); Contract.Ensures(Contract.Result().Order > 0); if (this.gsystem == null) { throw new InvalidOperationException("G-system is null."); } return this.gsystem; } set => this.gsystem = value; } /// Gets or sets modality. /// Property description. [XmlIgnore] public IGeneralStruct Modality { get; set; } /// Gets or sets level of the structure. Sum of digits. /// Property description. public int GLevel { get; set; } /// Gets or sets level of the structure. /// Property description. [XmlAttribute] public byte Level { get => this.level; set { this.level = value; this.SetProperty(GenProperty.Level, this.level); } } /// /// Gets or sets the occurrence - for statistical reasons (material,...). May be moved to properties... /// /// /// The occurrence. /// public int Occurrence { get; set; } /// Gets variability. /// Property description. public float Variability { get; private set; } /// Gets elements of the figure. /// Property description. [XmlIgnore] public Collection ElementList { get { Contract.Ensures(Contract.Result>() != null); if (this.elemList == null) { throw new InvalidOperationException("List of elements is null."); } return this.elemList; } } /// Gets list of differences. /// Property description. [XmlIgnore] public Collection DiffList { get { Contract.Ensures(Contract.Result>() != null); if (this.diffList == null) { this.MakeDifferences(); } if (this.diffList == null) { throw new InvalidOperationException("List of differences is null."); } return this.diffList; } } /// Gets differences. /// Property description. public string DiffSchema => this.DifferenceString(); /// Gets differences. /// Property description. public string PositiveElementSchema => this.PositiveElementString(); #endregion /// /// Gets the Structural Code. /// /// Returns value. /// Property description. public string GetStructuralCode { get { //// 2014/12 Time optimization - was prepared here (why not used?) if (string.IsNullOrEmpty(this.structuralCode)) { this.structuralCode = this.DetermineStructuralCode(); } return this.structuralCode; } } #region Static Functions /// Rotate the number, moving first bit to the end. /// Degree of system. /// Order of system. /// Number of instance. /// Returns value. [JetBrains.Annotations.PureAttribute] public static decimal TransposeLeft(byte givenDegree, byte order, decimal number) { var size = (decimal)Math.Pow(givenDegree, order); return (number * givenDegree) % size; } /// Returns empty number with only bit at position 'i' set. /// Element of system. /// Returns value. [JetBrains.Annotations.PureAttribute] public static long BitAt(byte element) { return (long)1 << element; } /// Returns if selected bit is ON. /// Number of instance. /// Element of system. /// Returns value. [JetBrains.Annotations.PureAttribute] public static bool ElementIsOn(decimal number, byte element) { return ((long)number & BitAt(element)) != 0; } /// /// Determine Level. /// /// System order. /// Structural Number. /// Returns value. [JetBrains.Annotations.PureAttribute] public static byte DetermineLevel(byte sysOrder, decimal number) { byte level = 0; for (byte e = 0; e < sysOrder; e++) { if (ElementIsOn(number, e)) { level++; } } return level; } #endregion #region Static operators //// TICS rule 7@526: Reference types should not override the equality operator (==) //// public static bool operator ==(FiguralStructure structure1, FiguralStructure structure2) { return object.Equals(structure1, structure2); } //// public static bool operator !=(FiguralStructure structure1, FiguralStructure structure2) { return !object.Equals(structure1, structure2); } //// but TICS rule 7@530: Class implements interface 'IComparable' but does not implement '==' and '!='. /// /// Implements the operator <. /// /// The object1. /// The object2. /// /// Returns value. /// public static bool operator <(FiguralStructure object1, FiguralStructure object2) { if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) { return object1.DecimalNumber < object2.DecimalNumber; } return false; } /// /// Implements the operator >=. /// /// The object1. /// The object2. /// /// Returns value. /// public static bool operator >=(FiguralStructure object1, FiguralStructure object2) { if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) { return object1.DecimalNumber >= object2.DecimalNumber; } return false; } /// /// Implements the operator <=. /// /// The object1. /// The object2. /// /// Returns value. /// public static bool operator <=(FiguralStructure object1, FiguralStructure object2) { if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) { return object1.DecimalNumber <= object2.DecimalNumber; } return false; } /// /// Implements the operator >. /// /// The object1. /// The object2. /// /// Returns value. /// public static bool operator >(FiguralStructure object1, FiguralStructure object2) { if (object1 != null && object2 != null && (object1.DecimalNumber > 0 || object2.DecimalNumber > 0)) { return object1.DecimalNumber > object2.DecimalNumber; } return false; } #endregion /// Makes a deep copy of the FiguralStructure object. /// Returns object. public override object Clone() { return new FiguralStructure(this.GSystem, this.GetStructuralCode); } #region Comparison /// Support sorting according to level and number. /// Object to be compared. /// Returns value. public override int CompareTo(object value) { if (!(value is FiguralStructure structure)) { return 0; } if (this.Level < structure.Level) { return -1; } return this.Level > structure.Level ? 1 : string.CompareOrdinal(this.ElementString(), structure.ElementString()); //// This kills the DataGrid //// throw new ArgumentException("Object is not a FiguralStructure"); } /// Test of equality. /// Object to be compared. /// Returns value. public override bool Equals(object obj) { //// check null (this pointer is never null in C# methods) if (object.ReferenceEquals(obj, null)) { return false; } if (object.ReferenceEquals(this, obj)) { return true; } if (this.GetType() != obj.GetType()) { return false; } return this.CompareTo(obj) == 0; } /// Support of comparison. /// Returns value. public override int GetHashCode() { return this.ElementString().GetHashCode(); } #endregion /// Test of emptiness. /// Returns value. [JetBrains.Annotations.PureAttribute] public virtual bool IsEmptyStruct() { return this.level == 0; } /// Validity test. /// Returns value. [JetBrains.Annotations.PureAttribute] public virtual bool IsValidStruct() { return true; } #region Element gettings /// Returns if selected bit is ON. /// Requested element. /// Returns value. [JetBrains.Annotations.PureAttribute] public bool IsOn(byte element) { return element < this.ElementList.Count && this.ElementList[element] != 0; } /// /// Determines whether the specified element is one. /// /// The element. /// /// true if the specified element is one; otherwise, false. /// [JetBrains.Annotations.PureAttribute] public bool IsOne(byte element) { return element < this.ElementList.Count && this.ElementList[element] == 1; } /// Returns if selected bit is OFF. /// Requested element. /// Returns value. [JetBrains.Annotations.PureAttribute] public bool IsOff(byte element) { if (element < this.ElementList.Count) { return this.ElementList[element] == 0; } return false; } /// Returns if selected bit is a start of tone. /// Requested element. /// Returns value. [JetBrains.Annotations.PureAttribute] public bool IsToneStart(byte element) { if (element < this.ElementList.Count) { return this.ElementList[element] == 1; } return false; } /// Returns if selected bit is a start of pause. /// Requested element. /// Returns value. [JetBrains.Annotations.PureAttribute] public bool IsPauseStart(byte element) { if (element < this.ElementList.Count) { return this.ElementList[element] == 2; } return false; } /// /// Returns corresponding binary structure. /// /// If set to true [break for rests]. /// /// Returns value. /// [JetBrains.Annotations.PureAttribute] public BinaryStructure BinaryStructure(bool breakForRests) { var sys = new GeneralSystem(2, this.GSystem.Order); var binStr = new BinaryStructure(sys, (string)null); for (byte e = 0; e < this.GSystem.Order; e++) { //// 2015/01 if (breakForRests) { if (this.IsOn(e)) { binStr.On(e); } } else { if (this.IsOne(e)) { binStr.On(e); } } } binStr.DetermineLevel(); return binStr; } /// /// Bits the array. /// /// if set to true [break for rests]. /// /// Returns value. /// public BitArray BitArray(bool breakForRests) { var bitArray = new BitArray(this.GSystem.Order); for (byte e = 0; e < this.GSystem.Order; e++) { if (breakForRests) { if (this.IsOn(e)) { bitArray[e] = true; } } else { if (this.IsOne(e)) { bitArray[e] = true; } } } return bitArray; } /// Returns number of bits in selected Range, that are ON. /// First element. /// Last element. /// Number of bits. [JetBrains.Annotations.PureAttribute] public byte IsOnInRange(byte elementFrom, byte elementTo) { byte s = 0; for (var e = elementFrom; e <= elementTo; e++) { if (this.IsOn(e)) { s++; } } return s; } #endregion #region Element settings /// /// Sets selected bit to value. /// /// Number of element. /// Value of element. public void SetElement(byte element, short value) { if (element < this.ElementList.Count) { this.ElementList[element] = value; } } /// /// Set Element List. /// /// The given list. public void SetElementList(Collection givenList) { this.elemList = givenList; this.CompleteFromElements(); this.DetermineBehavior(); } #endregion /// Determine and sets the level property. public void DetermineLevel() { this.Level = 0; this.GLevel = 0; var order = this.GSystem.Order; for (byte e = 0; e < order; e++) { if (e >= this.ElementList.Count || this.ElementList[e] <= 0) { continue; } this.GLevel += this.ElementList[e]; this.Level++; } this.Properties[GenProperty.Level] = this.Level; // used with Qualifiers } /// /// Determine and sets the level property. /// /// Given bit of the structure. /// Returns value. public byte LevelOfBit(byte givenBit) { if (givenBit == 0) { return 0; } var bitLevel = -1; //// byte order = this.GSystem.Order; for (byte e = 0; e <= givenBit; e++) { if (e < this.ElementList.Count && this.ElementList[e] > 0) { bitLevel++; } } var lev = (byte)((bitLevel >= 0) ? bitLevel : 0); return lev; } /// /// Determine and sets the level property. /// public void DetermineINumber() { decimal num = 0; if (this.ElementList.Count > 0) { var order = this.GSystem.Order; var degree = this.GSystem.Degree; for (var e = (short)(order - 1); e >= 0; e--) { if (e < this.ElementList.Count && num < decimal.MaxValue / degree) { //// Uff num = (num * this.GSystem.Degree) + (byte)this.ElementList[e]; } } } this.DecimalNumber = num; } /// Evaluate properties of the structure. Used in descendant objects. /// Must be virtual, because of call from StructuralVariety. public virtual void DetermineBehavior() { } /// Make list of differences. public void MakeDifferences() { this.diffList = new Collection(); if (this.ElementList.Count == 0) { return; } byte p = 0, r = 0; var order = this.GSystem.Order; for (byte e = 0; e < order - 1; e++) { if (e + 1 >= this.ElementList.Count) { continue; } p = (byte)this.ElementList[e]; r = (byte)this.ElementList[(byte)(e + 1)]; this.diffList.Add((short)(r - p)); } if (order - 1 < this.ElementList.Count) { p = (byte)this.ElementList[(byte)(order - 1)]; } if (this.ElementList.Count > 0) { r = (byte)this.ElementList[0]; } this.diffList.Add((short)(r - p)); } /// /// Complete From Elements. /// public virtual void CompleteFromElements() { this.DetermineLevel(); ////this.DetermineINumber(); this.ComputeVariability(); this.MakeDifferences(); } #region String representation /// List of figure elements. /// Returns value. public virtual string ElementString() { var s = new StringBuilder(); var cnt = this.ElementList.Count; var order = this.GSystem.Order; for (byte e = 0; e < cnt; e++) { s.Append(this.ElementList[e]); if (e < order - 1) { s.Append(","); } } return s.ToString(); } /// List of figure elements. /// Returns value. public string PositiveElementString() { const byte shift = 50; var s = new StringBuilder(); var cnt = this.ElementList.Count; for (byte e = 0; e < cnt; e++) { s.AppendFormat(CultureInfo.CurrentCulture, "{0,2}", (this.ElementList[e] + shift).ToString("D", CultureInfo.CurrentCulture.NumberFormat)); } return s.ToString(); } /// List of figure elements. /// Returns value. public string InverseElementString() { var s = new StringBuilder(); for (var e = (short)(this.GSystem.Order - 1); e >= 0; e--) { if (e >= this.ElementList.Count) { continue; } s.Append(this.ElementList[e]); if (e < this.GSystem.Order - 1) { s.Append(","); } } return s.ToString(); } /// List of figure Differences. /// Returns value. public string DifferenceString() { if (this.DiffList == null || this.DiffList.Count == 0) { return string.Empty; } var s = new StringBuilder(); s.Append("("); for (byte e = 0; e < this.GSystem.Order; e++) { if (e >= this.diffList.Count) { continue; } s.Append(this.diffList[e]); if (e < this.GSystem.Order - 1) { s.Append(","); } } s.Append(")"); return s.ToString(); } /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); //// s.Append("<" + this.Data.ToString(System.Globalization.CultureInfo.CurrentCulture.NumberFormat) + ">\t"); //// "D", System.Globalization.CultureInfo.CurrentCulture.NumberFormat s.Append("L" + this.Level.ToString("D", CultureInfo.CurrentCulture.NumberFormat)); s.Append(" "); s.Append(this.ElementString()); s.Append(" "); return s.ToString(); } #endregion #region Structural code /// Determine and sets the level property. /// Structural code. public void SetStructuralCode(string givenStructuralCode) { this.structuralCode = givenStructuralCode; if (string.IsNullOrWhiteSpace(this.structuralCode)) { return; } //// Temporary if (this.structuralCode.Length == 1) { return; } var structCode = this.structuralCode.Contains('*') ? UnpackCode(this.structuralCode) : this.structuralCode; this.Level = 0; this.GLevel = 0; this.elemList = new Collection(); //// byte order = this.GSystem.Order; //// givenStructuralCode.Count; var codes = structCode.Split(','); // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var code in codes) { if (string.IsNullOrEmpty(code)) { continue; } var element = short.Parse(code, CultureInfo.CurrentCulture); if (element < 0) { continue; } if (this.ElementList.Count >= this.GSystem.Order) { continue; //// 2013/01 } this.ElementList.Add(element); if (element <= 0) { continue; } this.Level += 1; this.GLevel += element; } //// Used by Qualifiers this.Properties[GenProperty.Level] = this.Level; } /// Determine and sets the level property. /// Returns value. public string DetermineStructuralCode() { var order = this.GSystem.Order; if (order <= 1) { return null; } //// 2014/12 Time optimization var sb = new StringBuilder(); foreach (var c in this.ElementList) { sb.Append(c.ToString(CultureInfo.CurrentCulture)); sb.Append(","); } sb.Remove(sb.Length - 1, 1); var structCode = sb.ToString(); /* Time optimization string structCode = this.ElementList[0].ToString(CultureInfo.CurrentCulture); for (byte e = 1; e < order; e++) { if (e >= this.ElementList.Count) { break; } structCode += "," + this.ElementList[e].ToString(CultureInfo.CurrentCulture); } */ //// bool packedCodes = true; return structCode.Length > 0 ? PackCode(structCode) : structCode; } #endregion #region Computation of properties /// Determine and sets the variability property. public void ComputeVariability() { var me = this.MeanElement(); var value = (float)0.0; var order = this.GSystem.Order; for (byte e = 0; e < order; e++) { if (e < this.ElementList.Count) { value = value + (float)Math.Pow(Math.Abs(this.ElementList[e] - me), 2.0); } } this.Variability = 100 * (float)Math.Sqrt(value) / order; // *100/(double)order; } #endregion /// Determine and sets the elements and level property. public void SetElements() { //// protected var num = this.DecimalNumber; this.Level = 0; this.GLevel = 0; this.elemList = new Collection(); var order = this.GSystem.Order; var degree = this.GSystem.Degree; for (byte e = 0; e < order; e++) { var rest = num % degree; if (rest >= 0) { this.ElementList.Add((byte)rest); if (e < this.ElementList.Count && this.ElementList[e] > 0) { this.GLevel += this.ElementList[e]; this.Level += 1; } } num = (num - rest) / this.GSystem.Degree; } //// Used by Qualifiers this.Properties[GenProperty.Level] = this.Level; } /// /// Similarities the value. /// /// The given structure. /// Returns value. public int SimilarityValue(FiguralStructure givenStructure) { var order = Math.Min(this.GSystem.Order, givenStructure.GSystem.Order); var value = 0; for (byte e = 0; e < order; e++) { if (e >= this.ElementList.Count || this.ElementList[e] <= 0) { continue; } if (e >= givenStructure.ElementList.Count || givenStructure.ElementList[e] <= 0) { continue; } if (this.ElementList[e] == givenStructure.ElementList[e]) { value++; } } return (100 * value) / order; } /// Mean distance of nonzero bits. /// Returns value. protected float MeanElement() { var order = this.GSystem.Order; var value = (float)this.GLevel / order; return value < 1 ? 1 : value; } /// Determine and sets the elements and level property. protected void SetElementsFromDifferences() { decimal num = 0; //// this.Data; this.Level = 0; this.GLevel = 0; this.elemList = new Collection(); this.diffList = new Collection(); short sum = 0; var order = this.GSystem.Order; var degree = this.GSystem.Degree; var halfDegree = (short)(degree / 2); for (byte e = 0; e < order; e++) { var eval = (byte)(num % degree); var diff = (short)(eval - halfDegree); this.diffList.Add(diff); sum += diff; this.ElementList.Add(sum); if (e >= this.ElementList.Count) { continue; } if (this.ElementList[e] != 0) { this.GLevel += this.ElementList[e]; this.Level += 1; } num = num / degree; } this.Properties[GenProperty.Level] = this.Level; // used with Qualifiers } #region Code packing /// /// Unpacks the code. /// /// The packed code. /// Returns value. private static string UnpackCode(string packedCode) { Contract.Requires(packedCode != null); //// 2014/12 Time optimization var sb = new StringBuilder(); //// string structCode = string.Empty; var packedCodes = packedCode.Split(','); Array.ForEach( packedCodes, pc => { if (pc.Contains('*')) { var position = pc.IndexOf('*'); var cnt = int.Parse(pc.Substring(0, position), CultureInfo.CurrentCulture); var code = pc.Substring(position + 1); for (var i = 1; i <= cnt; i++) { sb.Append(code); sb.Append(","); } } else { sb.Append(pc); sb.Append(","); } }); return sb.ToString(); } /// /// Packs the code. /// /// The code. /// Returns value. private static string PackCode(string structCode) { Contract.Requires(structCode != null); //// 2014/12 Time optimization var sb = new StringBuilder(); var codes = structCode.Split(','); var lastCode = codes[0]; var cnt = 0; Array.ForEach( codes, code => { if (string.CompareOrdinal(code, lastCode) != 0) { if (cnt > 1) { sb.Append(cnt.ToString(CultureInfo.CurrentCulture)); sb.Append("*"); } sb.Append(lastCode); sb.Append(","); lastCode = code; cnt = 0; } cnt++; }); if (cnt > 1) { sb.Append(cnt.ToString(CultureInfo.CurrentCulture)); sb.Append("*"); } sb.Append(lastCode); return sb.ToString(); } #endregion /* Time optimization - without the check /// /// Check Instance. /// private void CheckInstance() { if (this.gsystem == null) { throw new ArgumentException("Empty gsystem!"); } } */ } }